
#include "stdafx.h"


file_handle_t open_logfile( LPCTSTR filename )
{
	file_handle_t file;

#if defined WINDOWSSYSLOG_USE_CSTDLIB_FILE
	file.file = _tfsopen( filename, _T( "a+b" ), _SH_DENYWR );
#else
	file.file = CreateFile(
		filename,
		FILE_APPEND_DATA,
		FILE_SHARE_READ,
		nullptr,
		OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		nullptr
	);
#endif

	return file;
}

file_handle_t open_stdout( )
{
	file_handle_t file;

#if defined WINDOWSSYSLOG_USE_CSTDLIB_FILE
	file.file = stdout;
#else
	file.file = GetStdHandle( STD_OUTPUT_HANDLE );
#endif

	return file;
}

void close( file_handle_t file )
{
	if( file )
	{
#if defined WINDOWSSYSLOG_USE_CSTDLIB_FILE
		if( file.file != stdout )
			fclose( file.file );
#else
		if( file.file != GetStdHandle( STD_OUTPUT_HANDLE ) )
			CloseHandle( file.file );
#endif
		file = null_file_handle;
	}
}

size_t write( file_handle_t file, const byte* buffer, size_t buf_size )
{
#if defined WINDOWSSYSLOG_USE_CSTDLIB_FILE
	return fwrite( buffer, buf_size, 1, file.file );
#else
	DWORD count = 0;

	WriteFile(
		file.file,
		buffer,
		( DWORD ) buf_size,
		&count,
		nullptr
	);

	return count;
#endif
}

void flush( file_handle_t file )
{
	if( file )
	{
#if defined WINDOWSSYSLOG_USE_CSTDLIB_FILE
		fflush( file.file );
#else
		FlushFileBuffers( file.file );
#endif
	}
}


void unset_read_only_attrib( LPCTSTR file_name )
{
	DWORD file_attrib = GetFileAttributes( file_name );

	if( file_attrib != INVALID_FILE_ATTRIBUTES )
		if( file_attrib & FILE_ATTRIBUTE_READONLY )
			SetFileAttributes( file_name, file_attrib & ~FILE_ATTRIBUTE_READONLY );
}

void set_read_only_attrib( LPCTSTR file_name )
{
	DWORD file_attrib = GetFileAttributes( file_name );

	if( file_attrib != INVALID_FILE_ATTRIBUTES )
		if( file_attrib & ~FILE_ATTRIBUTE_READONLY )
			SetFileAttributes( file_name, file_attrib | FILE_ATTRIBUTE_READONLY );
}


bool get_reg_value( HKEY from, LPCTSTR name, DWORD* val )
{
	DWORD temp_data = 0, temp_data_size = sizeof( temp_data ), type = 0;

	DWORD Ret = RegQueryValueEx(
		from,
		name,
		nullptr,	// LPDWORD lpReserved	This parameter is reserved and must be NULL.
		&type,
		( LPBYTE ) &temp_data,
		&temp_data_size
	);

	if(
		Ret != ERROR_SUCCESS ||
		type != REG_DWORD
	)
		return false;

	*val = temp_data;

	return true;
}

bool get_reg_value( HKEY from, LPCTSTR name, TCHAR buffer[ ], DWORD buf_size )
{
	TCHAR temp_buf[ SYSLOG_MAX_PATH ];

	DWORD temp_data_size = sizeof( temp_buf ), type = 0;

	DWORD Ret = RegQueryValueEx(
		from,
		name,
		nullptr,	// LPDWORD lpReserved	This parameter is reserved and must be NULL.
		&type,
		( LPBYTE ) temp_buf,
		&temp_data_size
	);

	if(
		sizeof( temp_buf[ 0 ] ) == 2 &&
		temp_data_size % 2
	)
		return false;

	temp_data_size /= sizeof( temp_buf[ 0 ] ); // was in bytes, now in temp_buf[ ] elements

	if(
		Ret != ERROR_SUCCESS ||
		type != REG_SZ ||
		temp_buf[ temp_data_size - 1 ] != _T( '\0' ) ||
		buf_size < temp_data_size
	)
		return false;

	return _tcscpy_s( buffer, temp_data_size, temp_buf ) == 0; // _tcscpy_s( ) return value: zero if successful; otherwise, an error. (MSDN)
}


date_time get_system_time( )
{
	SYSTEMTIME dt_win;

	GetSystemTime( &dt_win );

	date_time dt;


	dt.year = dt_win.wYear;

#pragma warning( disable: 4244 )

	dt.month = dt_win.wMonth;
	dt.day = dt_win.wDay;

	dt.day_of_week = dt_win.wDayOfWeek;

	dt.hours = dt_win.wHour;
	dt.minutes = dt_win.wMinute;
	dt.seconds = dt_win.wSecond;

#pragma warning( default: 4244 )

	dt.msecs = dt_win.wMilliseconds;

	return dt;
}

date_time get_local_time( )
{
	SYSTEMTIME dt_win;

	GetLocalTime( &dt_win );

	date_time dt;


	dt.year = dt_win.wYear;

#pragma warning( disable: 4244 )

	dt.month = dt_win.wMonth;
	dt.day = dt_win.wDay;

	dt.day_of_week = dt_win.wDayOfWeek;

	dt.hours = dt_win.wHour;
	dt.minutes = dt_win.wMinute;
	dt.seconds = dt_win.wSecond;

#pragma warning( default: 4244 )

	dt.msecs = dt_win.wMilliseconds;

	return dt;
}


const TCHAR* dt_compare_string[ ] =
{
	_T( "yearly" ),
	_T( "monthly" ),
	_T( "daily" ),
	_T( "hourly" ),
	_T( "minutely" )
};


bool are_dates_the_same( date_time dt1, date_time dt2, date_time_compare compare )
{
	if( dt1.year != dt2.year )
		return false;

	if( compare == to_year )
		return true;


	if( dt1.month != dt2.month )
		return false;

	if( compare == to_month )
		return true;


	if( dt1.day != dt2.day )
		return false;

	if( compare == to_day )
		return true;


	if( dt1.hours != dt2.hours )
		return false;

	if( compare == to_hour )
		return true;


	if( dt1.minutes != dt2.minutes )
		return false;

	if( compare == to_minute )
		return true;


	return false;
}


int print_timestamp( char* buffer, size_t buf_size, date_time timestamp, bool date, bool time, bool msec )
{
	int ret1 = 0, ret2 = 0;

	if( date )
		if( ( ret1 = sprintf_s( buffer, buf_size, "%04d-%02d-%02d\t", timestamp.year, timestamp.month, timestamp.day ) ) < 0 )
			return ret1;

	if( time )
	{
		if( msec )
			ret2 = sprintf_s( buffer + ret1, buf_size - ret1, "%02d:%02d:%02d.%03d\t", timestamp.hours, timestamp.minutes, timestamp.seconds, timestamp.msecs );
		else
			ret2 = sprintf_s( buffer + ret1, buf_size - ret1, "%02d:%02d:%02d\t", timestamp.hours, timestamp.minutes, timestamp.seconds );

		if( ret2 < 0 )
			return ret2;
	}

	return ret1 + ret2;
}

int print_ip( char* buffer, size_t buf_size, IN_ADDR addr )
{
	return sprintf_s( buffer, buf_size, "%d.%d.%d.%d\t", addr.s_net, addr.s_host, addr.s_lh, addr.s_impno );
}

int print_facility_severity( char* buffer, size_t buf_size, int PRI )
{
	static const char* facilities[ ] =
	{
		"kernel",
		"user",
		"mail",
		"system",
		"security",
		"syslogd",
		"line printer",
		"network news",
		"UUCP",
		"clock",
		"security",
		"FTP",
		"NTP",
		"log audit",
		"log alert",
		"clock",
		"local0",
		"local1",
		"local2",
		"local3",
		"local4",
		"local5",
		"local6",
		"local7"
	};

	static const char* severities[ ] =
	{
		"Emergency",
		"Alert",
		"Critical",
		"Error",
		"Warning",
		"Notice",
		"Informational",
		"Debug"
	};

	if( 0 <= PRI && PRI <= 191 )
		return sprintf_s( buffer, buf_size, "%s\t%s\t", facilities[ PRI >> 3 ], severities[ PRI & 0x7 ] );
	else
		return sprintf_s( buffer, buf_size, "-\t-\t" );
}


bool regex_check( const char* regex, const char* checked_string )
{
#if defined ( WINDOWSSYSLOG_DEBUG )
	_ftprintf( WSL_DBG_STREAM, WSL_DBG_PREFIX _T( "regex_check( {%hs}, {%hs} )\n" ), regex, checked_string );
#endif
	for( ; *regex; regex++, checked_string++ )
	{
#if defined ( WINDOWSSYSLOG_DEBUG )
		_ftprintf( WSL_DBG_STREAM, WSL_DBG_PREFIX _T( " for( ... ): regex:{%hs}, checked_string:{%hs}\n" ), regex, checked_string );
#endif
		switch( *regex )
		{

		case '?':
			if( *checked_string == '\0' )	// '?' requires a character; an empty string does not match
				return false;
			else
				continue; // outer 'for'

		case '*':
		{
			while( *++regex == '*' )	// several asterisks in row have the same meaning as one, so skip them
				;

			if( *regex == '\0' )	// regex contains only asterisk(s), so it matches any string
				return true;

			do
			{
				if( *regex != '?' && *regex != '[' && *regex != '\\' )
					while( *checked_string && *checked_string != *regex )
						checked_string++;

				if( *checked_string == '\0' )
					return false;

				if( regex_check( regex, checked_string ) )
					return true;

			}
			while( *++checked_string );

			return false;
		}

		case '[':
		{
			bool matched = false;

			bool inversed = ( regex[ 1 ] == '^' );

			if( inversed )
				regex++;

			char prev = '\0';

			bool escaped = false;

			while( *++regex && ( escaped || ( *regex != ']' ) ) )
			{
#if defined ( WINDOWSSYSLOG_DEBUG )
				_ftprintf( WSL_DBG_STREAM, WSL_DBG_PREFIX _T( "  case_'['_while( ... ): regex:{%hs}, checked_string:{%hs}\n" ), regex, checked_string );
#endif
				if( !escaped && ( *regex == '\\' ) )
				{
					escaped = true;
					continue;
				}

				if( !escaped && ( *regex == '-' ) )
					switch( *++regex )
					{
					case '\0':
						return false;

					case ']':
						if( prev <= *checked_string )
						{
							matched = true;
							prev = '\xff';
						}
						continue; // while

					case '\\':
						if( *++regex == '\0' )
							return false;

					default:
						if( ( prev <= *checked_string ) && ( *checked_string <= *regex ) )
							matched = true;
					}
				else
					if( *checked_string == *regex )
						matched = true;

				escaped = false;
				prev = *regex;
			} // while

			if(
				prev == '\0' ||
				escaped ||
				*regex != ']' ||
				matched == inversed
			)
				return false;

			continue; // outer 'for'
		} // case '['

		case '\\':
			if( *++regex == '\0' )
				return *checked_string == '\0';

		default:
			if( *checked_string != *regex )
				return false;

		} // switch
	} // for

	return *checked_string == '\0';
}
